package ils.domain.catalog.model;

import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class BookRepositoryImpl implements BookRepository
{
    private static final Logger logger         = LoggerFactory.getLogger(BookRepositoryImpl.class);
    private static final String BOOKS_CAT_INCLUDE_SUB_CAT = "select book from Book as book where book.categoryName like :cat";
    private static final String BOOKS_CAT_WITHOUT_SUB_CAT = "select book from Book as book where book.categoryName = :cat";
    private static final String GET_BOOKS = "select book from Book as book where book.id in (:idList)";
    private SessionFactory sessionFactory;
    private CategoryRepository categoryRepository;

    @Override
    public Book createBook(Book book)
    {
        logger.debug("entered book factory");
        Session session = sessionFactory.getCurrentSession();
        int categoryId = book.getCategoryId();
        Category category = categoryRepository.getCategory(categoryId);
        session.save(book);
        Connection connection = session.connection();
        /*String insertSearch = "insert into books_search (id, title, author, description, categoryHierarchy) values(?, ?, ?, ?, ?)";
        try {
            PreparedStatement statement = connection.prepareStatement(insertSearch);
            statement.setInt(1, book.getId());
            statement.setString(2, book.getTitle());
            statement.setString(3, book.getAuthor());
            statement.setString(4, book.getDescription());
            statement.setString(5, category.getFullName());
            statement.execute();
        } catch (SQLException e) {
            logger.error("insertion into search table failed", e);
            session.getTransaction().rollback();
        }*/
        logger.debug("exiting book factory");
        return book;
    }

    @Override
    public List<Book> getBooksInCategory(boolean includeSubCategories, int categoryId)
    {
        Session session = sessionFactory.getCurrentSession();
        Category category = categoryRepository.getCategory(categoryId);
        String name = category.getFullName();
        if(includeSubCategories) return session.createQuery(BOOKS_CAT_INCLUDE_SUB_CAT).setString("cat", name + "%").list();
        else return session.createQuery(BOOKS_CAT_WITHOUT_SUB_CAT).setString("cat", name).list();
    }

    @Override
    public List<Book> searchBooks(String searchTerm, String type)
    {
        String sql_title = "select b.* from books b, books_search s where match(s.title) against(:term in natural language mode) and s.id = b.id";
        String sql_author = "select b.* from books b, books_search s where match(s.author) against(:term in natural language mode) and s.id = b.id";
        String sql_all = "select b.* from books b, books_search s where match(s.title,s.author,s.categoryHierarchy,s.topics) against(:term in natural language mode) and s.id = b.id";
        Session session = sessionFactory.getCurrentSession();
        SQLQuery query = null;
        if(type == null) query = session.createSQLQuery(sql_all);
        else if(type.equals("title")) query = session.createSQLQuery(sql_title);
        else if(type.equals("author")) query = session.createSQLQuery(sql_author);
        else throw new RuntimeException("unsupported search type. type: " + type);
        query.addEntity(Book.class);
        query.setString("term", searchTerm);
        return query.list();
    }
    public Map<Integer, Book> getBooks(List<Integer> bookIds)
    {
        Session session = sessionFactory.getCurrentSession();
        Query query = session.createQuery(GET_BOOKS);
        query.setParameterList("idList", bookIds);
        List<Book> books = query.list();
        Map<Integer, Book> bookMap = new HashMap<Integer, Book>();
        for(Book book : books)
        {
            bookMap.put(book.getId(), book);
        }
        return bookMap;
    }

    @Override
    public Book updateBook(Book book) {
        Session session = sessionFactory.getCurrentSession();
        session.update(book);
        return book;
    }

    @Override
    public Book getBook(int bookId)
    {
       Session session = sessionFactory.getCurrentSession();
       return (Book)session.load(Book.class, bookId);
    }

    public void setSessionFactory(SessionFactory sessionFactory)
    {
        this.sessionFactory = sessionFactory;
    }

    public void setCategoryRepository(CategoryRepository categoryRepository) {
        this.categoryRepository = categoryRepository;
    }
}
